/**
 * COMA Framework
 * v 1.0
 */

/*
https://code.google.com/hosting/settings
disier@gmail.com    sR8Gq9VX6BW7 
*/

/*********************************************************************************/
/**                              Global parameters                              **/
var lang = 'es';
var Business = null;
var e; /** debugging purposes */
var current_name = null; /* Object Name e.g. 'Ficha' */
var current_object = null /* The business object currently beeing edited. */
var current_filters = null; /* the last filters combination used */
var ORDER_BY = null; /* para distinguirla de varialbes locales */
var ORDER_DIRECTION = null;
var form_changed = false;
var debug = false;
var database = null; // Database object
// TODO Configurable
var items_per_page = 10;
var current_page = 0;

/**
 * Message types
 */
var MESSAGE_INFO = 1;
var MESSAGE_ERROR = 2;

/** 
 * Errors definintion
 *
 * all functions must implement try..catch structures. In the cath a message alert will be displayed to the user.
 * the text of the message will be the constant and the language.
 */
var E0001_es = 'El formulario $1 no tiene definido el atributo "object". No se puede saber sobre que elemento se está realizando la operación';
var E0002_es = 'No se ha podido cargar el fichero business.xml. Valide que el fichero está disponible y con la estructura adecuada.';

var M0001_es = 'Hay cambios pendientes de guardar';
var M0002_es = 'Hay cambios pendientes de guardar. ¿Desea cancelar de todas maneras?';
var M0003_es = '¿Está seguro que desea eliminar el elemento?';
var M0004_es = '(Sin cambios pendientes)';

function compose(errorId, param1) {
    var msg = eval(errorId + '_' + lang);
    msg = msg.replace('$1', param1);
    return msg;
}

function comaLog(text, type) {
    if (!debug)
        return;

    if (type == MESSAGE_ERROR)
        console.error(text);
    else
        console.log(text);
}

/**
 * Prepares a form before use. Detectx changes on values
 */
function prepareForm() {
    // Set the control for value changes
    form_changed = false;

    // Check there is a form with type=edit 
    if ($('form[type=edit]').length != 1) {
        throw 'Form is malformed';
    }

    // Add event listener for form changes
    $("input, select").change(function () {
        form_changed = true;
        $('#change').html(M0001_es);

        // Disable default cancel button action
        $("#cancel").removeAttr('onclick');

        comaLog('Removed default cancel action. Setting confirmation instead');

        // Enable cancel confirmation
        $("#cancel").confirm({
            text: M0002_es,
            confirm: function (button) {
                // do something
                cancelEdit();
            },
            cancel: function (button) {
                // do something
                return;
            },
            confirmButton: "Si",
            cancelButton: "No",
            post: true
        });
    });

    // Set alive the datepikers
    initializeDatepickers();

    // Set time mask format for time and money inputs
    $(".time").mask("99:99:99");
    $(".money").autoNumeric('init', {
        aSep: '.',
        aDec: ',',
        aSign: '€ '
    });

    // Associate enter key with send button
    $('input').keydown(function (e) {
        if (e.keyCode == 13) {
            $('.btn-primary').click();
        }
    });
    comaLog('Added enter key event to ' + $('input').length + ' inputs');

    // Enable delete confirmation
    $("#delete").confirm({
        text: M0003_es,
        confirm: function (button) {
            // do something
            deleteElement();
        },
        cancel: function (button) {
            // do something
            return;
        },
        confirmButton: "Si",
        cancelButton: "No",
        post: true
    });

    // Perform custom changes
    customDisplay();

}


/**
 * Clears search filters
 */
function clearFilters() {
    $('form')[0].reset();
    var element_name = $(Business).find('BusinessObject[name=' + current_name + ']').find('tablename').text();
    $('#' + element_name + '_list_results').empty();

    customLoadFilter();
}

/**
 * Page initialization
 */
$(document).ready(function () {

    if (!checkDatabaseConfig()) {
        $.removeCookie('usuarioRol');
        $.removeCookie('usuarioId');
        $.removeCookie('currentUser');
        Business = null;
        $('#sections').empty();
    }
    // executes when HTML-Document is loaded and DOM is ready
    comaLogin();
});


/**
 * Retrieves configuration and checks if the database is accessible
 * @param {Type}
 */
function checkDatabaseConfig() {
    // Initialize and set as global variable
    database = new Db();

    // Check database connection
    var message = '';
    var data;
    try {
        data = database.dbSelectSynch('config', null);
    } catch (e) {
        message = e.message;
    }

    if (data != null && data.error != null) {
        switch (data.error.code) {
        case 404:
            message = 'No se ha podido acceder a la información de base de datos.';
            break;
        case 204:
            message = 'No se encuentra la información de configuración';
            break;
        case 401:
            message = 'Existe un problema de credenciales para acceder a la base de datos';
            break;
        }
    }

    if (message != '') {
        $('#message_title').html('<span class="glyphicon glyphicon-warning-sign" aria-hidden="true"> Error</span>');
        $('#lyr_messages_text').html(message);
        $('#lyr_messages').modal();
        return false;
    }

    // Saves config
    for (var i = 0; i < data.length; i++) {
        eval(data[i].parameter + ' = \'' + data[i].value + '\'');

        // Check for default values
        if (typeof EXCEL_ENABLED === 'undefined')
            EXCEL_ENABLED = '0';
        if (typeof VERSION === 'undefined')
            VERSION = "1.0";
    }

    return true;
}

/**
 * General initialization of the page
 */
function comaInit() {
    comaLog('Initiating loading...');

    try {

        // For all <button> objects in the page, add the type="button" to avoid page reload
        $("button").attr("type", "button");
        comaLog('Added type="button" on al <button> controls');

        // Check if page is already loaded. In that case tis call is done after a partial reaload. Skip all logic reload
        if (Business != null) {
            comaLog('Reloading. Skipping comaInit()');
            loading(false);
            return;
        }

        // Load XML file
        // Put jquery in synchronous mode lo control loading process
        jQuery.ajaxSetup({
            async: false
        });

        Business = null;

        jQuery.ajax({
            url: 'business.xml?t=' + new Date().getTime(),
            success: function (xml) {
                Business = xml;
            }
        }).fail(function () {
            throw compose('E0002', null);
        });

        // Put jquery in asynchronous mode again
        jQuery.ajaxSetup({
            async: true
        });

        // Parse
        // TODO. Put Business into a JSON Object
        comaLog('business.xml loaded: ' + Business);

        // Display title and current user
        var title = $(Business).find('BusinessTitle').text() + ' - COMA Framework ' + $(Business).find('Business').attr('coma_version');
        $(document).prop('title', title);
        $('#project_title').html($(Business).find('BusinessTitle').text());
        var user = $.parseJSON($.cookie('currentUser'));
        var role_name = $(Business).find('BusinessRole[id="' + user.role + '"]').find('name').text()
        $('#lbl_user').html(user.nombre + ' (' + role_name + ')');

        // Draw menu
        var sections = $(Business).find('BusinessObject');
        for (var i = 0; i < sections.length; i++) {
            var section_name = $(sections[i]).attr('name');
            var section_id = $(sections[i]).find('tablename').text()
            comaLog(section_name);
            var link = $('<a></a>').attr('href', '#').attr('onclick', 'loadFilter(\'' + section_id + '\')').html(section_name);
            var li = $('<li></li>').attr('id', '1234').append(link);
            $('#sections').append(li);
        }

        displayMenu(user.isadmin, user.id);

        // Initialize datepickers
        initializeDatepickers();

        // Start with ficha filter
        loadFilter('ficha');

        // Perform custom inits
        customInit();

        comaLog('Loading finished');
        setStatus('Framework cargado', MESSAGE_INFO);
        loading(false);

    } catch (ex) {
        loading(false);
        errorMessage('Error cargando la pagina', 'Hubo un error durante la carga de la página', ex);
    }
}

function errorMessage(titulo, descripcion, excepcion) {
    $('#message_title').html('<span class="glyphicon glyphicon-warning" aria-hidden="true"> ' + titulo + '</span>');
    $('#lyr_messages_text').html(descripcion + '<br/><br/>Información detallada:<br/><code>' + excepcion + descripcion + '</code>');
    $('#lyr_messages').modal();
    loading(false);
}

/**
 * This function initizalizes datepickers inputs
 */
function initializeDatepickers() {
    try {
        comaLog('Initializing ' + $('.date').length + ' datepickers');
        $('.date').datepicker({
            pickDate: true,
            showToday: true,
            useStrict: true,
            dateFormat: "dd/mm/yy"
        });

        // Set checkbox value if date is picked or not
        $('.date').change(function (event) {
            var id = event.target.id;
            comaLog('Datepicker textbox cambiado: ' + id + ' con valor: ' + $('#' + event.target.id).val());
            if ($('#' + event.target.id).val() != null && $('#' + event.target.id).val() != '') {
                comaLog('Activando checkbox ' + id + 'Check');
                $('#' + id + 'Check').prop('checked', true);
            } else {
                $('#' + id + 'Check').prop('checked', false);
            }
        });

        // Set the pointer icon to the textbox
        $('.date').css('cursor', 'pointer');
    } catch (ex) {
        errorMessage('Error cargando la pagina', 'Hubo un error durante la inicializacion de los datepickers', ex);
    }
}

/**
 * Loads HTML page with filters and result list into index file
 */
function loadFilter(object_name) {
    try {
        loading(true);
        comaLog('loadFilter(' + object_name + ')');

        // Clears edit container
        $('#edit_container').fadeOut(400);
        $('#edit_container').empty();
        $('#list_container').fadeIn(200);

        // Put jquery in synchronous mode lo control loading process
        jQuery.ajaxSetup({
            async: false
        });

        // Load external HTML into container    
        $('#list_container').load('html/' + object_name + '-filter.html?t=' + new Date().getTime() + ' #filter', function (response, status, xhr) {
            if (status == "error") {
                throw new Error('No se encuentra el fichero HTML con el filtro para \'' + object_name + '\': ' + xhr.status + '. ' + xhr.statusText, MESSAGE_ERROR);
            } else {
                comaLog('Succesfully loaded ' + 'html/' + object_name + '-filter.html');
                initializeDatepickers();
                // For all <button> objects in the page, add the type="button" to avoid page reload
                $("button").attr("type", "button");
                comaLog('Added type="button" on al <button> controls');
            }
        });

        // Fill combos
        // TODO Do project independent
        var sufijos = new Array('Serie', 'Documental');
        fillCombos(object_name, sufijos, false);
        fillCombo('empresa', 'idEmpresa');

        // Associate enter key with send button
        $('input').keydown(function (e) {
            if (e.keyCode == 13) {
                $('.btn-primary').click();
            }
        });
        comaLog('Added enter key event to ' + $('input').length + 'inputs');

        // Put jquery in synchronous mode lo control loading process
        jQuery.ajaxSetup({
            async: true
        });

        // Restore form state
        $('#list_container').find('form').restoreForm();

        // Initialize pagination
        current_page = 1;

        // Perform project dependent form initializations
        customLoadFilter();
        loading(false);
    } catch (ex) {
        errorMessage('Error cargando la pagina', 'Hubo un error inicializando la pagina de búsqueda', ex);
    }
}

/**
 * TODO Esta funcion se ha creado porque no deja llamar a 'search' desde javascript por conflicto con algun elemento o palabra reservada. Me tire 1h...
 * @param {Type} event
 * @param {Type} page
 * @param {Type} order_by
 * @param {Type} order
 */
function launchSearch(event, page, order_by, order, excel_export) {
    if (order_by == null)
        order_by = order_by;
    if (order == null)
        order = ORDER_DIRECTION;
    if (excel_export == null)
        excel_export = false;
    search(event, page, order_by, order, excel_export);
}
/**
 * This function is called from a search form, from its 'search' button.
 *
 * the funciton automatically:
 *  1. detects the type of BusinessOfbject we are searching
 *  2. Retrive the values fo the filters to build query
 *  3. Retrieves de data from the database
 *  4. Gives control to callback
 *
 * @param {event} event
 * @param {integer} the number of the current page
 * @param {text} The sorting column.
 * @param {excel_export} true/false. Indicates that this is an export
 */
function search(event, page, order_by, order_direction, excel_export) {
    try {
        loading(true);
        comaLog('search(' + event + ', page:' + page + ',order_by:' + order_by + ', order_direction:' + order_direction + ', excel_export:' + excel_export + ') executed');

        // Check if it's a excel generation search
        if (excel_export == null)
            excel_export = false;

        // Check pagination
        if (page == null || page < 1)
            page = 1;
        current_page = page;
        var offset = (current_page - 1) * items_per_page;
        var limit = items_per_page;

        // If no order indicated, sort by first (id) column
        ORDER_BY = order_by;
        ORDER_DIRECTION = order_direction;
        if (ORDER_BY == null)
            ORDER_BY = 'id';
        if (ORDER_DIRECTION == null)
            ORDER_DIRECTION = 'asc';

        // Get form and searched object
        // If we come from "search" button, we take the form from the event. If we come from a next/prev link, we take the only form on page, wich is a filter
        var object_name = '';
        var form_id = '';
        if (event != null && event.target.form != null) {
            form_id = event.target.form.id;
        } else {
            form_id = $('form').attr('id');
        }
        object_name = $('#' + form_id).attr('object');
        current_name = object_name;

        if (object_name == null)
            throw compose('E0001', form_id);

        // Save form values
        $('#list_container').find('form').saveForm();

        comaLog('Search for object: ' + object_name);

        // Get filters values
        var filters = $('#' + form_id).find('[id*=filter_]');
        var texts = $('#' + form_id).find(':text');
        var checkboxes = $('#' + form_id).find(':checkbox');
        var radios = $('#' + form_id).find(':radio');
        comaLog('Detected filters: ' + filters.length + '. texts(' + texts.length + '), checkboxes(' + checkboxes.length + '), radios(' + radios.length + ')');

        // get tablename
        var table_name = $(Business).find('BusinessObject[name=' + object_name + ']').find('tablename').text();
        comaLog('Table name localized for object: ' + table_name);

        // Prepare query
        current_filters = new Array();
        var counter = 0;
        for (var i = 0; i < texts.length; i++) {
            if (texts[i].value == null || texts[i].value == '')
                continue;

            var name = texts[i].id;
            name = name.substring(7, 30); // remove 'filter_'

            // For dat filters we hace tho controls: Init and End. eg. fechaRecibidoInit and fechaRecibidoEnd
            if ($(texts[i]).hasClass('date')) {
                var date = new ComaUtil().DateToDB(texts[i].value);
                if (date == null) {
                    comaLog('Incorrect date on field ' + name + '. Discarding');
                    continue;
                }

                if (name.indexOf('Init') > -1) {
                    var field_name = name.substr(0, name.length - 4);
                    current_filters[counter] = "{'" + field_name + "','dgt','" + date + "'}";

                } else if (name.indexOf('End') > -1) {
                    var field_name = name.substr(0, name.length - 3);
                    current_filters[counter] = "{'" + field_name + "','dlw','" + date + "'}";
                } else {
                    comaLog('input ' + name + ' with class "date" does not specify "Init" or "End". Discarding');
                }
            } else {
                current_filters[counter] = "{'" + name + "','taprox','" + texts[i].value + "'}";
                comaLog('Adding filter: ' + current_filters[counter]);
            }
            counter++;
        }

        // Load custom filters if any
        current_filters = customFilters(current_filters);

        // Launch query
        if (excel_export)
            database.dbSelect(table_name, null, searchCallbackExcel, null, null, current_filters, null, null, null, null, order_by, order_direction);
        else
            database.dbSelect(table_name, null, searchCallback, null, null, current_filters, null, offset, limit, null, order_by, order_direction);

    } catch (ex) {
        errorMessage('Error cargando la pagina', 'Hubo un error durante la búsqueda', ex);
    }
}

/**
 * This function relaunch a search without user interaction, using last used filters.
 */
function relaunchSearch() {
    search(null, current_page);
}


/**
 * Search Callback
 *
 * This function is invoked when we get the response form the server
 *
 * 1. Retrieve response (in JSON format)
 * 2.
 * @param {Type}
 */
function searchCallback() {

    try {
        comaLog('searchCallback() executed');

        // Locate containing table
        var element_name = $(Business).find('BusinessObject[name=' + current_name + ']').find('tablename').text();
        $('#' + element_name + '_list_results').empty();

        // checks data is not null
        if (RETRIEVED_DATA == null || RETRIEVED_DATA.length == null) {
            comaLog('Empty data returned. Exiting');
            $('#' + element_name + '_list_results').html('<legend>No se han encontrado resultados</legend>');
            loading(false);
            return;
        }

        // Saves retrieved data in local variable to avoid to happen to be rewriten during processing
        var data = RETRIEVED_DATA;

        // Check how many rows we have
        comaLog('Retrieved ' + data.length + ' entries');
        if (data.length == 0) {
            comaLog('No results. Exiting...');
            $('#' + element_name + '_list_results').html('<legend>No se han encontrado resultados</legend>');
            return;
        }

        // Create table to display results
        var table = document.createElement("table");
        table.id = element_name + '_results_table';

        var thead = document.createElement("thead");
        var positions = new Array();

        // Draw table header from business definition
        var business_features = getBusinessFeatures(current_name);

        comaLog('Creating header with ' + business_features.length + ' business features makred as Listed:');
        var header_log = '';
        var header_discarded = '';
        for (var i = 0; i < business_features.length; i++) {
            var feature = business_features[i];

            // If the Feature is indicated to be listed on the XML, display it on the table header
            if (!feature.isListed()) {
                header_discarded += '[ ' + feature.Name + ' ]';
                continue;
            }
            var th = document.createElement("th");
            // Check if this column is ordered. for computed fields reference field is used to sort
            var header_html = '';
            if (feature.Sorteable) {
                var is_computed = (feature.DataType == 'computed');
                var order_field = (is_computed ? feature.ReferenceField : feature.FieldName);
                var is_order_column = order_field == ORDER_BY;
                // Build HTML code
                header_html += '&nbsp;';
                if (is_order_column) {
                    if (ORDER_DIRECTION == 'asc') {
                        header_html += '<div style="display:inline; cursor: pointer">';
                        header_html += '<a onclick="launchSearch(null, ' + current_page + ', \'' + order_field + '\',\'desc\')" style="color:black">';
                        header_html += feature.Name;
                        header_html += ' <img src="img/sort_up.png"></a></div>';
                    } else {
                        header_html += '<div style="display:inline; cursor: pointer">';
                        header_html += '<a onclick="launchSearch(null, ' + current_page + ', \'' + order_field + '\',\'asc\')" style="color:black">';
                        header_html += feature.Name;
                        header_html += ' <img style="" src="img/sort.png"></a></div>';
                    }
                } else {
                    header_html += ' <div style="display:inline; cursor: pointer">';
                    header_html += '<a onclick="launchSearch(null, ' + current_page + ', \'' + order_field + '\',\'asc\')" style="color:black">';
                    header_html += feature.Name;
                    header_html += ' <img style="opacity:0.3" src="img/sort.png"></a></div>';
                }
            } else
                console.log('Feature ' + feature.Name + ' not sorteable');
            th.innerHTML = header_html;

            header_log += '[ ' + feature.Name + ' ]';
            thead.appendChild(th);
            // Save position of feature in table
            positions.push(feature.Name);
        }
        comaLog(header_log);
        comaLog('Discarded columns (not listed): ');
        comaLog(header_discarded);
        table.appendChild(thead);

        var tbody = document.createElement("tbody");

        var retrieved_features = Object.keys(data[0]);
        comaLog('There are ' + business_features.length + ' features in business model');
        comaLog('There are ' + retrieved_features.length + ' features in retrieved data');

        // Iterate throught data
        for (var i = 0; i < data.length; i++) {
            var row_data = new Array();

            var log = '';

            // Iterate throught downloaded fields
            var position = 0;
            for (var j = 0; j < retrieved_features.length; j++) {

                // Get position of feature and column on table
                var cell_index_in_features = getFeaturePosition(business_features, retrieved_features[j]);
                if (cell_index_in_features == -1) {
                    console.error('Retrieved database feature ' + retrieved_features[j] + ' does not exists in model. discarding');
                    continue;
                }
                var cell_index_in_columns = -1;
                for (var k = 0; k < positions.length; k++) {
                    if (positions[k] == business_features[cell_index_in_features].Name) {
                        cell_index_in_columns = k;
                        break;
                    }
                }

                if (business_features[cell_index_in_features].isListed()) {
                    // Display data in its particular format
                    var cell_data;
                    switch (business_features[cell_index_in_features].DataType) {
                    case 'date':
                        cell_data = data[i][retrieved_features[j]];
                        if (cell_data == null || cell_data == 'null' || cell_data == 'undefined')
                            cell_data = '(vacio)';
                        else
                            cell_data = new ComaUtil().DBtoDateString(cell_data);
                        break;
                    case 'computed':
                        // With de computed data type, the representation of the feature is not done directly from data. A custom function is delegate to display
                        // the HTML code representing the feaure
                        cell_data = customRender(business_features[cell_index_in_features].FieldName, business_features, data[i])
                        break;

                    case 'bit':
                        cell_data = data[i][retrieved_features[j]];
                        if (cell_data == null || cell_data == 'null' || cell_data == 'undefined' || cell_data == '0' || cell_data == 0)
                            cell_data = 'No';
                        else
                            cell_data = 'Sí';
                        break;

                    default:
                        cell_data = data[i][retrieved_features[j]];
                        break;
                    }
                    comaLog('Cell ' + cell_index_in_columns + ': ' + cell_data);
                    row_data[cell_index_in_columns] = cell_data;
                }
            }
            // Build row and it cells
            var tr = document.createElement("tr");
            for (var j = 0; j < row_data.length; j++) {
                var td = document.createElement("td");
                // TODO: display custom fields display (por ejemplo, el estado de la ficha SONIGRAF, que depende de varios campos)
                td.innerHTML = row_data[j];
                tr.appendChild(td);
            }

            // Add edit action on last column
            var td = document.createElement("td");
            td.style.textAlign = 'right';
            td.innerHTML = '<button type="button" class="btn btn-default" onclick="edit(\'' + current_name + '\',\'' + row_data[0] + '\')"><span class=" + glyphicon glyphicon-pencil"></span> Editar</button>';
            tr.appendChild(td);
            tbody.appendChild(tr);
        }
        table.appendChild(tbody);

        comaLog('Appending results to ' + element_name + '_list_results');

        $('#' + element_name + '_list_results').append(table);

        // Give style
        $('#' + element_name + '_results_table').addClass("table");
        $('#' + element_name + '_results_table').addClass("table-hover");
        $('#' + element_name + '_results_table').addClass("table-striped");

        // Get the total elements on the server for this query
        var counters = database.getElementsCount(element_name, current_filters);
        var pagination_text;
        if (counters[0].total < items_per_page) {
            pagination_text = 'Mostrando ' + counters[0].total + ' elementos';
        } else if (counters[0].total < parseInt(current_page) * items_per_page) {
            pagination_text = 'Mostrando elementos del ' + ((current_page - 1) * items_per_page + 1).toString() + ' al ' + counters[0].total;
        } else {
            pagination_text = 'Mostrando elementos del ' + ((current_page - 1) * items_per_page + 1).toString() + ' al ' + ((parseInt(current_page) - 1) * items_per_page + parseInt(items_per_page)).toString() + ' de ' + counters[0].total;
        }

        // Pagination
        var pagination_ul = $('<ul />').attr('id', 'pagination_ul').addClass('pagination').appendTo($('#' + element_name + '_list_results'));
        // Calculations
        var page_previous = parseInt(current_page) - 1;
        var percentage = (((parseInt(current_page)) * parseInt(items_per_page)) / parseInt(counters[0].total)) * 100;
        var num_paginas = parseInt(counters[0].total / items_per_page) + 1;
        var page_next = parseInt(current_page) + 1;
        var page_last = parseInt(num_paginas) + 1;

        // First page icon
        var first_li = $('<li />')
            .html('<a class="custom_link" onclick="launchSearch(event,0)"><span class="glyphicon glyphicon-fast-backward" aria-hidden="true"></span>&nbsp;</a>')
            .appendTo(pagination_ul);
        var prev_li = $('<li />')
            .html('<a class="custom_link" onclick="launchSearch(event, ' + page_previous + ')"><span class="glyphicon glyphicon-backward" aria-hidden="true"></span>&nbsp;</a>')
            .appendTo(pagination_ul);
        var current_li = $('<li />').appendTo(pagination_ul);
        var current_span = $('<span />').css('display', 'inline').appendTo(current_li);
        var progress_div = $('<div />').addClass('progress').css('width', '400px').css('margin', '0px').appendTo(current_span);
        var progress_msg = $('<div />').addClass('progress-value').html(pagination_text).appendTo(progress_div);
        var progress_bar = $('<div />').addClass('progress-bar').addClass('coma-bar').css('width', percentage + '%').appendTo(progress_div);
        var goto_li = $('<li />').appendTo(pagination_ul);
        var goto_span = $('<span />').html('Ir a página ').appendTo(goto_li);

        // Excel icon
        if (EXCEL_ENABLED == '1') {
            $('<img />')
                .attr('src', 'img/excel.png')
                .click(function () {
                    launchSearch(event, 0, null, null, true);
                })
                .attr('title', 'Descargar todos los resultados en formato Excel')
                .css('style', 'pointer')
                .attr('id', 'btn_export')
                .appendTo(
                    $('<span />')
                    .css('height', '28px')
                    .appendTo(
                        $('<li />').appendTo(pagination_ul)
                    )
            );
        }

        // Combo page select

        var cmb_goto = $('<select />').attr('id', 'cmb_goto').appendTo(goto_span);
        cmb_goto.change(function () {
            launchSearch(event, $('#cmb_goto').val(), ORDER_BY, ORDER_DIRECTION, false);
        });
        for (var j = 1; j < num_paginas + 1; j++)
            cmb_goto.append($('<option>').text(j).val(j));
        cmb_goto.val(current_page);

        // Items per page combo
        var cmb_pages = $('<select />').attr('id', 'cmb_pages').appendTo($('<span />').html('Items por página ').appendTo($('<li />').appendTo(pagination_ul)));
        var pagination_options = [10, 20, 50, 100, 200, 500];
        for (var j = 0; j < pagination_options.length; j++) {
            $('<option />').attr('value', pagination_options[j]).html(pagination_options[j]).appendTo(cmb_pages);
            console.log('loop');
        }
        cmb_pages.val(items_per_page);
        cmb_pages.change(function () {
            items_per_page = $('#cmb_pages').val();
            launchSearch(event, current_page);
        });


        var next_li = $('<li />')
            .html('<a class="custom_link" onclick="launchSearch(event, ' + page_next + ')"><span class="glyphicon glyphicon-forward" aria-hidden="true"></span>&nbsp;</a>')
            .appendTo(pagination_ul);
        var last_li = $('<li />')
            .html('<a class="custom_link" onclick="launchSearch(event, ' + page_last + ')"><span class="glyphicon glyphicon-fast-forward" aria-hidden="true"></span>&nbsp;')
            .appendTo(pagination_ul);

        if (current_page == 1) {
            first_li.addClass('disabled').html('<a ><span class="glyphicon glyphicon-fast-backward"></span>&nbsp;</a>');
            prev_li.addClass('disabled').html('<a ><span class="glyphicon glyphicon-backward"></span>&nbsp;</a>');
        }
        if (counters[0].total < parseInt(current_page) * items_per_page) {
            next_li.addClass('disabled').html('<a>&nbsp;<span class="glyphicon glyphicon-forward"></span></a>');
            last_li.addClass('disabled').html('<a>&nbsp;<span class="glyphicon glyphicon-fast-forward"></span></a>');
        }

        loading(false);
        setStatus('Listado completo', MESSAGE_INFO);

    } catch (ex) {
        errorMessage('Error cargando la pagina', 'Hubo un error procesando los datos del servidor', ex);
    }
}

/**
 * This callback is for searches executed to create the Excel file
 * @param {Type}
 */
function searchCallbackExcel() {
    try {
        comaLog('searchCallbackExcel() executed');

        // Locate containing table
        var element_name = $(Business).find('BusinessObject[name=' + current_name + ']').find('tablename').text();

        // checks data is not null
        if (RETRIEVED_DATA == null || RETRIEVED_DATA.length == null) {
            comaLog('Empty data returned. Exiting');
            loading(false);
            return;
        }

        // Saves retrieved data in local variable to avoid to happen to be rewriten during processing
        var data = RETRIEVED_DATA;

        // Check how many rows we have
        comaLog('Retrieved ' + data.length + ' entries');
        if (data.length == 0) {
            comaLog('No results. Exiting...');
            loading(false);
            return;
        }

        // Draw table header from business definition
        var business_features = getBusinessFeatures(current_name);

        // Declare columns, based on the first row
        var columns = new Array();
        for (var i = 0; i < business_features.length; i++) {
            var feature = business_features[i];

            // For computed features, the curstom render translates its values
            if (feature.DataType == 'computed') {
                for (var j = 0; j < data.length; j++) {
                    var set = data[j];
                    var content = customRenderExcel(feature.FieldName, null, set);
                    eval('data[j].' + feature.FieldName + ' = \'' + content + '\'');
                }
            }

            // For FK features, search the referenced table to get the name to display instead of id
            if (feature.DataType == 'fk') {
                var referenced = feature.FieldName.substring(2).toLowerCase();
                var data2 = database.dbSelectSynch(referenced);

                for (var j = 0; j < data.length; j++) {
                    var id = eval('data[j].' + feature.FieldName);
                    for (var k = 0; k < data2.length; k++) {
                        if (data2[k].id == id) {
                            eval('data[j].' + feature.FieldName + ' = data2[k].nombre;');
                            console.log('referencia: ' + data2[k].nombre);
                            break;
                        }
                    }
                }
            }
            // Set cell data type
            var datatype = 'string';
            switch (feature.DataType) {
            case 'date':
                datatype = 'date';
                break;
            case 'int':
            case 'money':
                datatype = 'number';
                break;
            }
            // Append the new column to the model
            columns.push({
                headertext: feature.Name,
                datatype: datatype,
                datafield: feature.FieldName,
                ishidden: false
            });
        }

        // Create the Excel, uin a uri Base64 string
        var uri = $("#excel_export").battatech_excelexport({
            containerid: "excel_export",
            datatype: 'json',
            dataset: data,
            columns: columns,
            returnUri: true,
            worksheetName: 'Hoja1'
        });

        loading(false);

        $('#message_title').html('<span class="glyphicon glyphicon-download" aria-hidden="true"> Descarga</span>');
        $('#lyr_messages_text').html('El listado en formato Excel se ha generado y puede descargarlo con <a target="_blank" download="export.xls" href="' + uri + '">en este enlace</a>');
        $('#lyr_messages').modal();

    } catch (ex) {
        errorMessage('Error cargando la pagina', 'Hubo un error procesando los datos del servidor para la generación del Excel', ex);
    }
}


/**
 * This function is called from a checkbox along with a datepicker is checked or unchecked. It enable or disables the date picker.
 * @param {Type} event
 */
function setDatepicker(event) {
    comaLog('setDatepicker() invoked');

    // Get the datepicker control
    var checkbox = event.target;
    var date_control_name = $('#' + checkbox.id).attr('control');
    if (date_control_name == null || date_control_name == '') {
        console.error('setDatepicker() checkbox ' + checkbox.id + ' does not have target control set');
        return;
    }
    var date_control = $('#' + date_control_name);
    comaLog('checkbox changed for datepicker: ' + date_control_name);

    // Change de datepicker
    if (checkbox.checked) {
        date_control.disabled = false;
        date_control.val(new ComaUtil().DateToString(new Date()));
        $('#' + checkbox.id).prop('checked', true);
    } else {
        date_control.disabled = false;
        date_control.val('');
        $('#' + checkbox.id).prop('checked', false);
    }
}


/**
 * This function is invoked when the user wants to view/edit a business object
 
 * @param {String} ObjectName. BusinessObject name. eg "ficha" 
 * @param {Int} Object id
 */
function edit(object_name, id) {
    try {
        loading(true);
        comaLog('edit(' + object_name + ', ' + id + ')');

        current_name = object_name;

        if (id != null && id != -1 && id != '') // Edit existing object
        {

            // Get the BusinessObject table name for query
            var table_name = $(Business).find('BusinessObject[name=' + object_name + ']').find('tablename').text();
            comaLog('The table name for ' + object_name + ' is ' + table_name);
            // Select on the database
            database.dbSelect(table_name, id, editCallback);
        } else { // Create new object
            // Create an empty BusinessObject from its features, as if it was returned from database
            var features = getBusinessFeatures(object_name);
            var object = new Object();
            for (var i = 0; i < features.length; i++) {
                eval('object.' + features[i].FieldName + ' = null');
                comaLog('object.' + features[i].FieldName + ' = \'\'');
            }
            object.id = -1;
            comaLog('Created empty object for editing with  ' + features.length + ' features: ' + object.toString());
            RETRIEVED_DATA = object;
            // Simulate a database callback
            editCallback();

        }
    } catch (ex) {
        setStatus('Hubo un error cargando los datos: ' + ex, MESSAGE_ERROR);
        throw ex;
    }
}



function editCallback() {
    try {
        comaLog('editCallback()');

        // Cehck we just retrieved one item
        // TODO Control errors on retrieved data
        if (RETRIEVED_DATA == null) {
            comaLog('Empty data returned. Exiting');
            loading(false);
            return;
        }
        if (RETRIEVED_DATA['id'] == null) {
            comaLog('No JSON object with Id retrieved. Exiting');
            loading(false);
            return;
        }

        // Saves retrieved data in local variable to avoid to happen to be rewriten during processing
        // TODO: Perform additional checks on retrieved object
        current_object = RETRIEVED_DATA;

        // Clears filter layer
        $('#edit_container').fadeIn(400);
        $('#list_container').fadeOut(400);
        $('#list_container').empty();

        // Put jquery in synchronous mode lo control loading process
        jQuery.ajaxSetup({
            async: false
        });

        // Load edit html page to index.html
        $('#edit_container').load('html/' + current_name.toLowerCase() + '-edit.html?t=' + new Date().getTime() + ' #edit_form', function (response, status, xhr) {
            if (status == "error") {
                console.error('Sorry but there was an error: ' + xhr.status + " " + xhr.statusText);
                setStatus('Error cargando HTML: ' + xhr.status + " " + xhr.statusText, MESSAGE_ERROR);
                return;
            } else {
                initializeDatepickers();
            }
        });

        // Get business features
        var business_features = getBusinessFeatures(current_name);

        // Fill combos
        for (var i = 0; i < business_features.length; i++) {
            if (business_features[i].DataType != 'fk')
                continue;

            comaLog('Filling combo for feature ' + business_features[i].FieldName);

            var reference_name = business_features[i].FieldName.substring(2).toLowerCase();
            var combo = $('#' + business_features[i].FieldName);
            if (combo == null) {
                console.error('Control for field ' + business_features[i].FieldName + ' not found. Not filled');
                continue;
            }
            var combo_data = database.dbSelectSynch(reference_name);
            comaLog('Returned ' + combo_data.length + ' values for combo');
            $(combo).empty();
            //$('<option />', {
            //    val: '',
            //    text: '(Seleccione uno)'
            //}).appendTo(combo);

            // TODO: the field for the combo text must not be hardcoded. Only 'id' field is the same on all tables.
            // TODO: Esta iteracion es muy odlschool. usar el foreach de jquery para estas cosas.
            for (var j = 0; j < combo_data.length; j++) {
                combo.append($('<option>').text(combo_data[j].nombre).val(combo_data[j].id));
            }
        }

        // For each value, find its control and fill value
        for (var key in current_object) {
            var attrName = key;
            var attrValue = current_object[key];

            // control for errors
            if (!$('#' + attrName).length) {
                console.error('control "' + '#' + attrName + '" not found. Skipping.');
                continue;
            }
            var feature = getFeaureByName(business_features, attrName);
            if (feature == null) {
                console.error('No business feature with name ' + attrName + '. Skipping.');
                continue;
            }

            // Depending on dtaa type the display method is different
            switch (feature.DataType) {
            case 'date':
                if (attrValue == null) {
                    $('#' + attrName).val('');
                    $('#' + attrName + 'Check').attr('checked', false);
                } else {
                    date = new Date(attrValue);
                    $('#' + attrName + 'Check').attr('checked', true);
                    $('#' + attrName).val(new ComaUtil().DBtoDateString(attrValue));
                }
                break;

            case 'float':
                // TODO: Some formatting
                $('#' + attrName).val(attrValue);
                break;

            case 'fk':
                // Get the referenced Object from field name. idObject = fk of 'Object'
                var referenced_object = feature.FieldName.substring(2);
                comaLog(feature.FieldName + 'References to ' + referenced_object);
                $('#' + attrName).val(attrValue);
                break;

            case 'money':
                // Add class 'money' to the field and initilizes masl
                $('#' + attrName).addClass('money');
                $('#' + attrName).autoNumeric('init', {
                    aSep: '.',
                    aDec: ',',
                    aSign: '€ '
                });
                $('#' + attrName).autoNumeric('set', attrValue);
                break;

            case 'bit':
                if (attrValue == '1')
                    $('#' + attrName).prop('checked', true);
                else
                    $('#' + attrName).prop('checked', false);
                break;

            default:
                $('#' + attrName).val(attrValue);
                break;
            }

            comaLog('Set value ' + attrValue + ' on ' + attrName + '. Feature Name: ' + feature.Name + '. Data type: ' + feature.DataType);
        }

        // Inform if 
        if (current_object['id'] > 0)
            $('#lbl_id').html(current_object['id']);
        else
            $('#lbl_id').html('nuevo/a');

        // Put jquesy in asynchronous mode again
        jQuery.ajaxSetup({
            async: true
        });

        // Apply role restrictions to the different features froups
        var user_role = $.parseJSON($.cookie('currentUser')).role
        var ACLs = $(Business).find('BusinessRole[id=' + user_role + ']').find('RoleAccess');
        for (var i = 0; i < ACLs.length; i++) {
            var feature_group = $(ACLs[i]).find('FeatureGroupId').text();
            var permission = $(ACLs[i]).find('access').text();
            switch (permission.toUpperCase()) {
            case 'N':
                $($('div[feature_group=' + feature_group + ']').children()[1]).addClass('collapse');
                comaLog('Removed access for feature_group ' + feature_group);
                break;
            case 'R':
                $($('div[feature_group=' + feature_group + ']').children()[1]).removeClass('collapse');
                $($('div[feature_group=' + feature_group + ']').children()[1]).find(":input").prop("disabled", true);
                comaLog('Removed write access for feature_group ' + feature_group);
                break;
            case 'W':
                $($('div[feature_group=' + feature_group + ']').children()[1]).removeClass('collapse');
                $($('div[feature_group=' + feature_group + ']').children()[1]).find(":input").removeAttr("disabled");
                comaLog('Granted access for feature_group ' + feature_group);
                break;
            default:
                console.error('Incorrect permission set in business.xml for role ' + user_role + ': ' + permission);
                break;
            }
        }

        // Prepare edit form
        prepareForm();

        loading(false);
        setStatus('Elemento mostrado', MESSAGE_INFO);
    } catch (ex) {
        errorMessage('Error cargando la pagina', 'Hubo un error cargando los datos para su edición', ex);
    }
}


/**
 * This function gets the business logic, and try to fill all combos linked to that feautre with satellite tables
 * @Suffixes {Array}  Ann array of suffixes. Maybe the same feature is linked to more than on combo. E.g: table 'employee' may fill 'employeeChief' and 'employeeFired'. Null or empty for no suffices. E
 * @Strict. Do not display errors on log if combo is not found. In a filter screen maybe only some of the 'fk' features are used.
 */
function fillCombos(object_name, suffixes, strict) {
    comaLog('fillCombos(' + object_name + ',' + suffixes + ',' + strict + ')');

    // Get business features
    var business_features = getBusinessFeatures(object_name);
    comaLog(business_features.length);

    // Fin feature of type 'fk', to find it's combo.
    for (var i = 0; i < business_features.length; i++) {
        if (business_features[i].DataType != 'fk')
            continue;

        comaLog('Filling combo for feature ' + business_features[i].FieldName);

        if (suffixes == null || suffixes.length == 0) {
            var reference_name = business_features[i].FieldName.substring(2).toLowerCase();
            fillCombo(reference_name, business_features[i].FieldName);

        } else {
            var reference_name = business_features[i].FieldName.substring(2).toLowerCase();
            for (var j = 0; j < suffixes.length; j++) {
                fillCombo(reference_name, business_features[i].FieldName + suffixes[j]);
            }
        }
    }
}

/**
 * Fills a combo
 * @param {String} reference_name the name of the combo, for example Serie
 * @param {String} FeatureFieldName database name, for example idSerie
 */
function fillCombo(reference_name, feature_field_name) {
    var combo = $('#' + feature_field_name);
    if (combo == null) {
        console.error('Control for field ' + feature_field_name + ' not found. Not filled');
        return null;
    }
    var combo_data = database.dbSelectSynch(reference_name);
    comaLog('Returned ' + combo_data.length + ' values for combo');
    $(combo).empty();
    //$('<option />', {
    //    val: '',
    //    text: '(Seleccione uno)'
    //}).appendTo(combo);

    // TODO: the field for the combo text must not be hardcoded. Only 'id' field is the same on all tables.
    // TODO: Esta iteracion es muy odlschool. usar el foreach de jquery para estas cosas.
    for (var j = 0; j < combo_data.length; j++) {
        combo.append($('<option>').text(combo_data[j].nombre).val(combo_data[j].id));
    }
}

/**
 * This functions saves modified data
 *
 * 1. Detect the businessObject edited
 * 2. Creates an instance of the business object
 * 3. Retrieve all HTML form values into every BusinessObject feature
 * 4. Invokes the database
 *
 */
function saveObject() {
    try {
        loading(true);
        comaLog('saveObject()');

        // Check if we have current object
        if (current_object == null || current_object['id'] == null) {
            console.error('Save called with current_object empty');
            loading(false);
            return;
        }

        // save values on object
        var features = getBusinessFeatures(current_name);
        for (var i = 0; i < features.length; i++) {
            var feature = features[i];
            if (!$('#' + feature.FieldName).length) {
                console.error('Not found field with feature name ' + feature.FieldName + '. Skipping');
                continue;
            }

            var value = $('#' + feature.FieldName).val();
            if (typeof value == 'string' || value instanceof String)
                value = value.trim();

            switch (feature.DataType) {
            case 'date':
                if (value == '' || value == null) {
                    current_object[feature.FieldName] = null;
                } else {
                    current_object[feature.FieldName] = new ComaUtil().DateToDB(value);
                }
                break;
            case 'fk':
                if (value == '' || value == null) {
                    current_object[feature.FieldName] = null;
                } else {
                    current_object[feature.FieldName] = value;
                }
                break;
            case 'money':
                value = $('#' + feature.FieldName).autoNumeric('get');
                if (value == null || value == '') {
                    current_object[feature.FieldName] = 0.00;
                } else {
                    current_object[feature.FieldName] = value;
                }
                break;

            case 'bit':
                value = ($('#' + feature.FieldName).is(':checked') == true ? '1' : '0');
                current_object[feature.FieldName] = value;
                break;

            case 'text':
                // We do not let update texts with null because there are not updated. 
                if (value == null)
                    value = '';
                current_object[feature.FieldName] = value;
                break;
            default:
                current_object[feature.FieldName] = value;
                break;
            }

            // Remove null values when creating new objects
            if (current_object['id'] == -1 || current_object['id'] == null)
                if (value == null || value == '') {
                    comaLog('Remove empty field on creation: ' + feature.FieldName);
                    eval('delete current_object.' + feature.FieldName + ';');
                }

            comaLog('object.' + feature.FieldName + ' = "' + value + '" (' + feature.DataType + ');');
        }

        // Save object, synchronously
        database.dbUpdate(current_name.toLowerCase(), current_object, saveObjectCallback);

    } catch (ex) {
        errorMessage('Error guardando los datos', 'Hubo un error guardando los datos. Por favor, verifique que los datos sean correctos y no contengan carácteres especiales.', ex);
    }
}


/**
 * Callback for save action
 * @param {Type}
 */
function saveObjectCallback() {
    try {

        // Check result

        // Return and launch last search
        loadFilter(current_name.toLowerCase());
        relaunchSearch();

        loading(false);
        setStatus('Elemento guardado', MESSAGE_INFO);

    } catch (ex) {
        errorMessage('Error guardando los datos', 'Hubo un error guardando los datos. Por favor, verifique que los datos sean correctos y no contengan carácteres especiales.', ex);
    }

}


/**
 * displays the loading layer
 *
 * @param {Type} on
 */
var spinner = null;

function loading(on) {
    if (on) {
        $("#ajax_loader").empty();
        var opts = {
            lines: 17, // The number of lines to draw
            length: 33, // The length of each line
            width: 10, // The line thickness
            radius: 41, // The radius of the inner circle
            corners: 1, // Corner roundness (0..1)
            rotate: 0, // The rotation offset
            direction: 1, // 1: clockwise, -1: counterclockwise
            color: '#000', // #rgb or #rrggbb or array of colors
            speed: 1.2, // Rounds per second
            trail: 60, // Afterglow percentage
            shadow: false, // Whether to render a shadow
            hwaccel: false, // Whether to use hardware acceleration
            className: 'spinner', // The CSS class to assign to the spinner
            zIndex: 2e9, // The z-index (defaults to 2000000000)
            top: '50%', // Top position relative to parent
            left: '50%' // Left position relative to parent
        };
        //$('#loading_img').hide();
        var target = document.getElementById('ajax_loader');
        spinner = new Spinner(opts).spin();
        target.appendChild(spinner.el);
        $("#ajax_loader").show();
    } else {
        if (spinner != null)
            spinner.stop();
        spinner = null;
        $("#ajax_loader").hide();
    }
}

function setStatus(message_text, message_type) {
    switch (message_type) {
    case MESSAGE_INFO:
        var color = 'black';
        if (!debug)
            return;
        break;
    case MESSAGE_ERROR:
        var color = 'red';
        break;
    default:
        var color = 'black';
        if (!debug)
            return;
        break;
    }

    $(' < span > < /span>').addClass('status_message').css('color', color).html(message_text).prependTo('#status_bar').delay(2000).fadeOut(5000);
}

/** Edit cancellation.This    function is called after cormirmation is asked based on form changes * /
 */
function cancelEdit() {
    // Back to previous list screen 
    loadFilter(current_name.toLowerCase());
    relaunchSearch();
}

/**
 * Delete function. Deletes current object
 */
function deleteElement() {
    try {
        comaLog('deleteElement()');
        loading(true);

        // Check if we have current object
        if (current_object == null || current_object['id'] == null) {
            console.error('deleteElement called with current_object empty');
            loading(false);
            return;
        }
        if (current_object['id'] == -1) {
            comaLog('Delete was invoked when creating a new object. Just return');
            loading(false);
            cancelEdit();
            return;
        }

        // Do database query
        database.dbDeleteSynch(current_name.toLowerCase(), current_object.id);

        // Return and launch last search
        loadFilter(current_name.toLowerCase());
        relaunchSearch();

        loading(false);
        setStatus('Elemento eliminado', MESSAGE_INFO);

    } catch (ex) {
        errorMessage('Error eliminando los datos', 'Hubo un error eliminando los datos', ex);
    }

}


/**
 * Given a Feature list
 * @param {Type} <BusinessFeatures>
 * @param {Type} FieldName
 */
function getFeaturePosition(features, field_name) {
    for (var i = 0; i < features.length; i++) {
        if (features[i].FieldName == field_name)
            return i;
    }
    return -1;
}

/**
 * Given
 */
function getFeaureByName(features, field_name) {
    for (var i = 0; i < features.length; i++) {
        if (features[i].FieldName == field_name)
            return features[i];
    }
    return null;
}

/**
 * Returns the list of feature number. This is needed on several display logic
 */
function getBusinessFeatures(object_name) {
    try {
        comaLog('getBusinessFeatures(' + object_name + ')');

        var business_features = new Array();
        var feature_number = $(Business).find('BusinessObject[name=' + object_name + ']').find('Feature').length;

        // Append Id first pseudo-field
        business_features[0] = new BusinessFeature('Id', 'id', 1, null, 'int', null, 'yes');

        for (var i = 0; i < feature_number; i++) {
            var xml_feat = $(Business).find('BusinessObject[name=' + object_name + ']').find('Feature')[i];
            // Retreive feature info
            var is_listed = $(xml_feat).find('displaylisted').text();
            var feat_name = $(xml_feat).find('name').text();
            var feat_field = $(xml_feat).find('fieldname').text();
            var data_type = $(xml_feat).find('data_type').text();
            var reference_field = $(xml_feat).find('reference_field').text();
            var sorteable = $(xml_feat).find('sorted').text();
            var feature = new BusinessFeature(feat_name, feat_field, is_listed, i + 1, data_type, reference_field, sorteable);

            business_features[i + 1] = feature;
        }

        return business_features;
    } catch (ex) {
        console.error('getBusinessFeatures(). Error retriving features: ' + ex);
        throw 'No se pueden obtener las características del objeto. Es posible que en business.xml falte información';
        return null;
    }
}




/**
 * General initialization of the page
 */
function comaLogin() {
    comaLog('Initiating loading...');
    $('#menu').hide();

    try {
        if ($.cookie('currentUser') != null) {
            // User is logged
            var user = $.parseJSON($.cookie('currentUser'));
            comaLog('Usuario en cookies');
            comaInit();
        } else {
            // Display login
            // Put jquery in synchronous mode lo control loading process
            jQuery.ajaxSetup({
                async: false
            });
            $('#login_container').load('html/login.html');
            jQuery.ajaxSetup({
                async: true
            });

            // Associate enter key with send button
            $('input').keydown(function (e) {
                if (e.keyCode == 13) {
                    $('.btn-primary').click();
                }
            });

            // If 'remember me' coockie exists, display username and password
            if ($.cookie('usuarioRecordar') != null && $.cookie('usuarioRecordar') == 'true') {
                $('#usuario').val($.cookie('usuarioLogado'));
                $('#password').val($.cookie('usuarioPassword'));
            }

            comaLog('Added enter key event to ' + $('input').length + ' inputs');
        }

        comaLog('Login finished');
        loading(false);

    } catch (ex) {
        errorMessage('Error al iniciar', 'Hubo un error procesando el login del usuario', ex);
    }
}

function encriptarPassword(password) {
    //var encrypted = CryptoJS.AES.encrypt($('#password').val(), "Secret Passphrase");
    //var decrypted = CryptoJS.AES.decrypt(encrypted, "Secret Passphrase");
    //var original = decrypted.toString(CryptoJS.enc.Utf8);
    var hash = CryptoJS.MD5(password);
    return hash.toString(CryptoJS.enc.Base64);
}

function verify(event) {
    try {
        e = event;
        // comaLog(document.getElementById('usuario').value + "," + document.getElementById('password').value);
        // Select on the database
        var table_name = 'usuarios';
        var filtro = new Array();
        filtro[0] = "{'usuario','taprox','" + $('#usuario').val() + "'}";
        filtro[1] = "{'password','taprox','" + encriptarPassword($('#password').val()) + "'}";

        changeAllPasswords = false;
        database.dbSelect(table_name, null, verifyCallback, null, null, filtro);
    } catch (ex) {
        errorMessage('Error durante el login', 'Hubo un error al enviar las credenciales al servidor', ex);
    }
}

var changeAllPasswords;

function encriptarPasswords(event) {
    try {
        e = event;
        var table_name = 'usuarios';
        changeAllPasswords = true;
        database.dbSelect(table_name, null, verifyCallback);
    } catch (ex) {
        errorMessage('Error durante el login', 'Hubo un error al enviar las credenciales al servidor', ex);
    }
}

/**
 * Search Callback
 *
 * This function is invoked when we get the response form the server
 *
 * 1. Retrieve response (in JSON format)
 * 2.
 * @param {Type}
 */
function verifyCallback() {

    debug = true;

    try {
        console.log('verificarCallback() executed');

        // checks data is not null
        if (RETRIEVED_DATA == null || RETRIEVED_DATA.length == null) {
            comaLog('Empty data returned. Exiting');
            $('#group_username').addClass('has-error');
            $('#group_password').addClass('has-error');
            errorMessage('Login incorrecto', 'No ha sido posible acceder al sistema con las credenciales suministradas. Verifique el el nombre de usuario y la contraseña son correctos. En caso de persistir refresque la página', '');
            return;
        }

        // Saves retrieved data in local variable to avoid to happen to be rewriten during processing
        var data = RETRIEVED_DATA;

        // Check how many rows we have
        comaLog('Retrieved ' + data.length + ' entries');
        if (data.length == 0) {
            comaLog('No results. Exiting...');
            $('#group_username').addClass('has-error');
            $('#group_password').addClass('has-error');
            errorMessage('Login incorrecto', 'No ha sido posible acceder al sistema con las credenciales suministradas. Verifique el el nombre de usuario y la contraseña son correctos. En caso de persistir refresque la página', '');
            return;
        }

        // Find logging user
        var retrieved_features = Object.keys(data[0]);
        for (var i = 0; i < data.length; i++) {
            if ($('#usuario').val() == data[i].usuario && encriptarPassword($('#password').val()) == data[i].password) {

                displayMenu(data[i].isadmin, data[i].id)
                comaLog('usuario encontrado');

                // Guardar cookies
                $.cookie('usuarioLogado', $('#usuario').val());
                $.cookie('usuarioPassword', $('#password').val());
                $.cookie('usuarioRecordar', document.getElementById("recordarUsuario").checked, {
                    expires: 7
                });

                var user = new User(data[i].id, data[i].usuario, data[i].password, data[i].nombre, data[i].surname, data[i].role, data[i].isadmin);
                $.cookie('currentUser', JSON.stringify(user));
                comaLog('Saving as a cookie: ' + JSON.stringify(user));
                debug = false;
                comaLog('succesfull login. Invoking coma Init');
                comaInit();

                // Log last login
                user.lastlogin = new Date().toISOString().slice(0, 19).replace('T', ' ');
                database.dbUpdateSynch('usuarios', user);

                $('#group_password').removeClass('has-error');
                $('#group_username').removeClass('has-error');
                $('#login_container').empty();
                if (changeAllPasswords == false)
                    return;
            }
            if (changeAllPasswords == true) {
                var new_user = new Object();
                new_user.id = data[i].id;
                new_user.password = encriptarPassword(data[i].password);
                database.dbUpdateSynch('usuarios', new_user);
            }
        }
        // If we are here the login information is incorrect
        $('#group_password').addClass('has-error');
        $('#group_username').addClass('has-error');
        comaLog('Login error');

        debug = false;

    } catch (ex) {
        errorMessage('Error durante el login', 'Hubo un error durante la comprobación de las credenciales', ex);
    }
}

function displayMenu(IsAdmin, Id) {
    $('#menu').show();

    if (IsAdmin == '1')
        $("#controlAdmin").show();
    else
        $("#controlAdmin").hide();
    $("#controlCambioPassword").show();
    /*var enlace = "edit('Usuarios','" + Id + "')";
    $("#accionCambioPassword").attr({
        'onclick': enlace
    });*/
    $("#controlCerrarSesion").show();
    $("#menuConfiguracion").addClass("dropdown-menu");
}

function recordarUsuario() {
    if ($.cookie('usuarioRecordar') == 'true') {
        $.cookie('usuarioRecordar', 'true', {
            expires: 7
        });
        return true;
    }
    return false;
}

function cerrarSesion() {
    $.removeCookie('usuarioRol');
    $.removeCookie('usuarioId');
    $.removeCookie('currentUser');
    Business = null;
    $('#sections').empty();
    $('#list_container').empty();
    comaLogin();
    window.location.href = './';
}

function changePassword() {
    try {
        comaLog('changePassword()');
        // Check the old password is good
        var user = $.parseJSON($.cookie('currentUser'));
        if (user.password != encriptarPassword($('#old_password').val())) {
            alert('La contraseña es incorrecta');
            return;
        }
        // Check the new password
        if ($('#new_password').val() != $('#retype_password').val()) {
            alert('Las contraseñas no son idénticas');
            return;
        }
        // Check the new password
        if ($('#new_password').val() == "" || $('#retype_password').val() == "") {
            alert('Las contraseñas deben tener mínimo un caracter');
            return;
        }
        $('#modal_password').removeClass('in');
        var new_user = new Object();
        new_user.id = user.id;
        new_user.password = encriptarPassword($('#new_password').val());
        database.dbUpdateSynch('usuarios', new_user);

        alert('Contraseña cambiada con éxito. Por favor, vuelva a identificarse');
        cerrarSesion();
    } catch (ex) {
        errorMessage('Error realizando la operación', 'Hubo un error cambiando la contraseña en el servidor', ex);
    }
}